package com.common.sdk.net.connect.http.util;

import com.android.sohu.sdk.common.toolbox.LogUtils;
import com.common.sdk.net.connect.interfaces.IQueueFull;

import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * LIFO List
 * 后进先出同步队列，如果List的size大于maxSize，则删除尾部的一个Item，然后在头部添加一个Item，每次take一条Item，会从头部获取。
 * 
 * @author boyang116245
 * @param <E>
 */
public class BlockingLimitedStackQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>{

    private static final String TAG = "BlockingLimitedStackQueue";

    public final int maxSize;

    private IQueueFull<E> queueFull;
    
    /** Condition for waiting takes */
    private final Condition notEmpty;

    public BlockingLimitedStackQueue(int maxSize) {
        this(maxSize, null);
    }

    public BlockingLimitedStackQueue(int maxSize, IQueueFull<E> queueFull) {
        super();
        this.maxSize = maxSize;
        this.queueFull = queueFull;
        lock = new ReentrantLock();
        notEmpty = lock.newCondition();
    }

    public List<E> mList = Collections.synchronizedList(new ArrayList<E>());

    /** Main lock guarding all access */
    final ReentrantLock lock;

    /**
     * get list size
     * 
     * @return
     */
    public synchronized int size() {
        return mList.size();
    }

    @Override
    public synchronized boolean contains(Object item) {

        if (!mList.isEmpty()) {
            if (mList.contains(item)) {
                return true;
            }
        }
        return false;
    }
    
    private void enqueue(E item) {
    	E removeItem = null;
        int size = mList.size();
        if (size >= maxSize) {
            int pos = mList.size() - 1;
            removeItem = mList.remove(pos);
            if (queueFull != null) {
                queueFull.queueFullWhenOffer(removeItem);
            }
        }
        LogUtils.d(TAG, "size is : " + mList.size());
        mList.add(0, item);
        notEmpty.signal();
    }
    
    private E dequeue() {
    	if (!mList.isEmpty()) {
            return mList.remove(0);
        }
    	return null;
    }

    @Override
    public boolean offer(E item) {

        final ReentrantLock lock = this.lock;
        lock.lock();

        try {
            enqueue(item);
            
            return true;

        } finally {
            lock.unlock();
        }
    }

    @Override
    public E poll() {

        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return dequeue();
        } finally {
            lock.unlock();
        }

    }

    @Override
    public E peek() {
    	final ReentrantLock lock = this.lock;
    	lock.lock();
    	try {
    		return mList.get(0);
    	} finally {
    		lock.unlock();
    	}
    }

    @Override
    public Iterator<E> iterator() {
        return mList.iterator();
    }

	@Override
	public int drainTo(Collection<? super E> c) {
		return drainTo(c, Integer.MAX_VALUE);
	}

	@Override
	public int drainTo(Collection<? super E> c, int maxElements) {
		if (c == null) {
			return 0;
		}
		if (c == this) {
            throw new IllegalArgumentException();
		}
        if (maxElements <= 0) {
            return 0;
        }
        
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	
        	int n = Math.min(maxElements, mList.size());
        	int i = 0;
        	Iterator<E> it = mList.iterator();
        	while(i<n) {
        		E e = it.next();
        		c.add(e);
        		it.remove();
        		i++;
        	}
        	return n;
        	
        } finally {
        	lock.unlock();
        }
        
	}

	@Override
	public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
		throw new UnsupportedOperationException();
	}

	@Override
	public E poll(long timeout, TimeUnit unit) throws InterruptedException {
		throw new UnsupportedOperationException();
	}

	@Override
	public void put(E e) throws InterruptedException {
		// 不阻塞
		offer(e);
	}

	@Override
	public int remainingCapacity() {
		return maxSize - mList.size();
	}

	@Override
	public E take() throws InterruptedException {
		final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (mList.size() == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
	}
}
